%{
/**********************************************************************
 * Katarzyna Ciechańska, v.2.0.0
 * 
 * Plik z regułami dla skanera flex, wersja >= 2.5.33.
 * 
 * Uwagi:
 * - skaner nie rozpoznaje wielkości znaków
 *********************************************************************/

#include <iostream>
#include <string>
#include <memory>
#include "Yystype.h"        // typy tokenów
#include "FlexScanner.h"
#include "BisonParser.h"    // rozpoznawane tokeny

// wskazanie na wlasciwa klase skanera - flex spodziewa sie yyFlexLexer
#define SCANNER    static_cast<FlexScanner *>(this)

//#define DEBUG

// wejście buforowane i cachowane z dokładnością do ostatniej linii
#ifdef YY_INPUT
#   undef YY_INPUT
#endif // YY_INPUT
#define YY_INPUT(buffer, result, max_size)                   \
        ( result = SCANNER->readInput(buffer, max_size) )

// rolę funkcji yylex spełni FlexScanner::scan ( potrzebna, aby
// dodać parametr YYSTYPE *, wymagany przez bison++
#ifdef YY_DECL
    #undef YY_DECL
#endif
#define YY_DECL \
    int FlexScanner::scan( YYSTYPE *yylval )

using namespace kc_sf;

/*
%option c++                         // flex++ - interfejs C++ ( => bezpieczny dla wątków )
%option stack                       // stos warunków początkowych
%option never-interactive batch     // wyłaczenie trybu interaktywnego ( => input wczytywany w porcjach )
%option nowarn                      
%option nounput noyywrap noyyget_lineno noyyset_lineno // opcje przetwarzania strumienia wejściowego
%option case-insensitive            // wielkość znaków nie wazna
*/
%}

    /* opcje skanera */
%option c++
%option stack
%option never-interactive batch
%option nowarn
%option nounput noyywrap noyyget_lineno noyyset_lineno
%option case-insensitive

    /* stany skanera */
%s START
%s HEADERS
%x BODY
%s UNSTRUCT
%s COMMENT
%x QUOTE
%s ADDRESS
%s DOMAIN
%s DATE
%s CONTENT
    
    /* tokeny */
ALPHA       ([a-zA-Z])
DIGIT       ([0-9])
WSP         ([ \t])
CRLF        (\r|\n|\r\n)
ANY         (.*)

	/*  RFC 2045 */
no-ws-ctrl      ([\001-\010\013\014\016-037\177])

text            ([\001-\010\013\014\016-\037!-\177])
ftext           ([!-9;-~])
atext           ([!#-'*+\-/-9=?A-Z^-`a-z{-~,])
dtext           (["(),.:;<@])
qtext           ([\001-\010\013\014\016-\037!#-@A-Z\[\]-`a-z{-\177])
bcharsnospace   ([a-zA-Z0-9'()+_,-./:=?])
bchars          ([a-zA-Z0-9 '()+_,-./:=?])
token           ([-!#-'*+.0-9A-Z^-`a-z{-~])
ctext           ([\001-\010\013\014\016-037!-'*-@A-Z\[\]-`a-z{-\177])
encoded_text    ([!->@-~])
zones           ([A-IK-Za-ik-z])

%%

    /* reguly */
	
<INITIAL,HEADERS,BODY>{

    ^("Date"|"Resent-Date")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( DATE );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_DATE );
    }
    ^("From"|"Resent-From")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_FROM );
    }
    ^("Sender"|"Resent-Sender")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_SENDER );
    }
    ^("Reply-To"|"Resent-Reply-To")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_REPLY_TO );
    }
    ^("To"|"Resent-To")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_TO );
    }
    ^("Cc"|"Resent-Cc")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_CC );
    }
    ^("Bcc"|"Resent-Bcc")/(":") { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_BCC );
    }
    ^("Message-ID"|"Resent-Message-ID")/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_MID );
    }
    ^"In-Reply-To"/(":")    { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_IN_REPLY_TO );
    }
    ^"References"/(":") { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_REFERENCES );
    }
    ^"Subject"/(":")    { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( UNSTRUCT );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_SUBJECT );
    }
    ^"Comments"/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( UNSTRUCT );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_COMMENTS );
    }
    ^"Keywords"/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_KEYWORDS );
    }
    ^"Return-Path"/(":")    { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_PATH );
    }
    ^"MIME-Version"/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( HEADERS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_VERSION );
    }
    ^"Content-Type"/(":")   { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( CONTENT );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_CONTENT_TYPE );
    }
    ^"Content-Transfer-Encoding"/(":")  { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( CONTENT );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_TRANSFER_ENC );
    }
    ^"Content-Description"/(":")    { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( UNSTRUCT );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_DESCRIPTION );
    }
    ^"Content-ID"/(":") { 

		#ifdef DEBUG
			std::cout << "HEADER: " << YYText();
		#endif // DEBUG
        yy_push_state( ADDRESS );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_CID );
    }
    ^{ftext}+/(":") { 

        if( YY_START == BODY )
        {
            // boundary
            std::string beg = YYText();
            if( beg.size() > 1 && beg[0] == beg[1] && beg[0] == '-')
            {
                // boundary-end
                unsigned int size = beg.size();
                if( size > 2 && beg[size - 1] == beg[size - 2] &&
                    beg[size - 1] == '-' )
                {   
                    BEGIN(BODY);
			#ifdef DEBUG
				std::cout <<"\n^^ BOUNDARY_END:" << YYText();
			#endif // DEBUG
                    return newToken(yylval, YYText(), yy::BisonParser::token::BOUNDARY_END );
                }
                BEGIN(HEADERS);
			#ifdef DEBUG
				std::cout << "\n^^ BOUNDARY:" << YYText();
			#endif // DEBUG
                return newToken(yylval, YYText(), yy::BisonParser::token::BOUNDARY ); 
            }
            // tekst wiadomosci
			#ifdef DEBUG
				std::cout << "<text1>" << YYText() << "</text1>";
			#endif // DEBUG
            return newToken(yylval, YYText(), yy::BisonParser::token::TEXT );
        }

        //nieznany typ naglówka
		#ifdef DEBUG
			std::cout << "HEADER ?: " << YYText();
		#endif // DEBUG
        yy_push_state( UNSTRUCT );
        yy_push_state( START );     
        return newToken(yylval, YYText(), yy::BisonParser::token::H_UNKNOWN );
    }
}

<START>{
    : {
		#ifdef DEBUG
			std::cout << ":";   
		#endif // DEBUG
        yy_pop_state();
        return newToken(yylval, YYText(), yy::BisonParser::token::COLON );
    }
}

    /* pusta linia po nagłówkach, przed body */
<INITIAL,HEADERS>^{CRLF} {
	#ifdef DEBUG
		std::cout << "----------->END of HEADERS\n" << std::endl;
	#endif // DEBUG
    BEGIN(BODY);
        
    return newToken(yylval, YYText(), yy::BisonParser::token::EOH );
}

<ADDRESS,HEADERS>{
    \.  {
		#ifdef DEBUG
			std::cout << ".";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::DOT );
	}
    \[  {
		#ifdef DEBUG
			std::cout << "[";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::LSQB );
	}
    \]  {
		#ifdef DEBUG
			std::cout << "]";
		#endif // DEBUG
		BEGIN(HEADERS);
		return newToken(yylval, YYText(), yy::BisonParser::token::RSQB );
	}
    \<  {
		#ifdef DEBUG
			std::cout << "<";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::LTRB );
	}
    \>  {
		#ifdef DEBUG
			std::cout << ">";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::RTRB );
	}
    @   {
		#ifdef DEBUG
			std::cout << "@";
		#endif // DEBUG
		BEGIN(DOMAIN);
		return newToken(yylval, YYText(), yy::BisonParser::token::AT );
	}
    ;   {
		#ifdef DEBUG
			std::cout << ";";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::SEMICOLON );
	}

    /* encoded-word */
    "=?"{token}*"?"{token}*"?"({encoded_text}{0,69})"?="    {
		#ifdef DEBUG
			std::cout << "<encoded-word>" << YYText() << "</encoded-word>";
		#endif // DEBUG
		/* tu wyluskac z niego tokeny i utworzyc liste */
		return newToken(yylval, YYText(), yy::BisonParser::token::ENCODED_WORD );
	}               
}

<ADDRESS,DATE,HEADERS>{
    :   {
		#ifdef DEBUG
			std::cout << ":";   
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::COLON );
	}
    ,   {
		#ifdef DEBUG
			std::cout << ",";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::COMMA );
	}
}

<HEADERS,DATE>{DIGIT}+ {
	#ifdef DEBUG
		std::cout << "<number>" << YYText() << "</number>";
	#endif // DEBUG
    // !!
//  if(getSign() ) { setSign(false); BEGIN(HEADERS); }
    return newToken(yylval, YYText(), yy::BisonParser::token::NUMBER );
}

<DOMAIN>{dtext}+ {
    BEGIN(HEADERS);
	#ifdef DEBUG
		std::cout << "<dtext>" << YYText() << "</dtext>";
	#endif // DEBUG
    return newToken(yylval, YYText(), yy::BisonParser::token::DTEXT );
}

<DATE>{
    (\+|\-) {
		#ifdef DEBUG
			std::cout << "<sign>" << YYText() << "</sign>";
		#endif // DEBUG
        return newToken(yylval, YYText(), yy::BisonParser::token::SIGN );
    }
    ("Mon"|"Tue"|"Wed"|"Thu"|"Fri"|"Sat"|"Sun") {
		#ifdef DEBUG
			std::cout << "<day>" << YYText() << "</day>";
		#endif // DEBUG
        return newToken(yylval, YYText(), yy::BisonParser::token::DAY );
    }
    ("Jan"|"Feb"|"Mar"|"Apr"|"May"|"Jun"|"Jul"|"Aug"|"Sep"|"Oct"|"Nov"|"Dec") {
		#ifdef DEBUG
			std::cout << "<month>" << YYText() << "</month>";
		#endif // DEBUG	
        return newToken(yylval, YYText(), yy::BisonParser::token::MONTH );
    }
    {zones} {
		#ifdef DEBUG
			std::cout << "<zone>" << YYText() << "</zone>";
		#endif // DEBUG
        return newToken(yylval, YYText(), yy::BisonParser::token::OBS_ZONE );
    }
}

<ADDRESS,DOMAIN,HEADERS>{atext}+ {
	#ifdef DEBUG
		std::cout << "<atext>" << YYText() << "</atext>";
	#endif // DEBUG
    BEGIN(HEADERS);
    return newToken(yylval, YYText(), yy::BisonParser::token::ATEXT );
}

<CONTENT>{
    \/  {
		#ifdef DEBUG
			std::cout << "/";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::SLASH );  
	}
    =   {
		#ifdef DEBUG
			std::cout << "=";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::EQUAL );  
	}
    ;   {
		#ifdef DEBUG
			std::cout << ";";
		#endif // DEBUG
		return newToken(yylval, YYText(), yy::BisonParser::token::SEMICOLON );
	}
    "boundary=\""{qtext}*"\"" {
		std::string boundary = YYText();
		size_t quote_pos = boundary.find_first_of( '\"' );
		SCANNER->fs_specials.push( boundary.substr( ++quote_pos, boundary.size() - quote_pos - 2 ) );
		#ifdef DEBUG
			std::cout << "<boundary>" << SCANNER->fs_specials.top() << "</boundary>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::TOKEN );  
        }
    "multipart"/\/  {
		#ifdef DEBUG
			std::cout << "multipart";
		#endif // DEBUG		
	            return newToken(yylval, YYText(), yy::BisonParser::token::MULTIPART );  
        }
    {token}+    {
		#ifdef DEBUG
			std::cout << "<token>" << YYText() << "</token>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::TOKEN );  
	}
    /* encoded-word */
    "=?"{token}*"?"{token}*"?"({encoded_text}{0,69})"?="    {
		#ifdef DEBUG
			std::cout << "<encoded-word>" << YYText() << "</encoded-word>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::ENCODED_WORD );
	}                               
}

    /* nieustrukturyzowany */
<UNSTRUCT>{

    {text}* {
		#ifdef DEBUG
			std::cout << "<unstr>" << YYText() << "</unstr>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::UNSTRUCTURED );
	}

    /* encoded-word, nastepniki ze wzgledu na niemozliwosc dopasowania sie do tej reguly bez nich */
    "=?"{token}*"?"{token}*"?"({encoded_text}{0,69})"?="/[ \t\r\n]  {
		#ifdef DEBUG
			std::cout << "<unstr encoded-word>" << YYText() << "</encoded-word>";
		#endif // DEBUG		
		/* tu wyluskac z niego tokeny i utworzyc liste */
		return newToken(yylval, YYText(), yy::BisonParser::token::ENCODED_WORD );
	}           
}

    /* ciało wiadomości */
<INITIAL,BODY>{
    "--"(({bchars}){0,69}{bcharsnospace})"--" { 
		BEGIN(BODY);

		std::string minuses = "--", 
			boundary = SCANNER->fs_specials.empty() ? "" :
				minuses + SCANNER->fs_specials.top() + minuses;

		if( boundary != YYText() )
			return newToken(yylval, YYText(), yy::BisonParser::token::TEXT );

		#ifdef DEBUG
			std::cout <<"\n^^^ BOUNDARY_END:" << YYText();
		#endif // DEBUG		
		SCANNER->fs_specials.pop();
		return newToken(yylval, YYText(), yy::BisonParser::token::BOUNDARY_END );
	}
    "--"(({bchars}){0,69}{bcharsnospace}) { 
		std::string minuses = "--", 
			boundary = SCANNER->fs_specials.empty() ? "" :
				minuses + SCANNER->fs_specials.top();
		if( boundary != YYText() )
			return newToken(yylval, YYText(), yy::BisonParser::token::TEXT );

		BEGIN(HEADERS);
		#ifdef DEBUG
			std::cout << "\n^^^ BOUNDARY:" << YYText();
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::BOUNDARY ); 
	}
}

    /* body = *(*998text CRLF) *998text */
<BODY>{
    \r$	{
		#ifdef DEBUG
			std::cout << "<body crlf>" << std::endl;
		#endif // DEBUG		
		return newToken(yylval, "\r\n", yy::BisonParser::token::CRLF );
	}
    \n              /* pomijane */
    {WSP}+	{
		#ifdef DEBUG
			std::cout << "<wsp>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::WSP );
	}
    ({text}){0,998} {
		#ifdef DEBUG
			std::cout << "<text>" << YYText() << "</text>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::TEXT );
	}
}
    
<QUOTE>{
    /* koniec cytatu */
    \"	{
		#ifdef DEBUG
			std::cout << "\"</quote>";
		#endif // DEBUG		
		BEGIN(HEADERS);
		return newToken(yylval, YYText(), yy::BisonParser::token::RDQUOTE  );
	}
    {WSP}+	{   
		return newToken(yylval, YYText(), yy::BisonParser::token::WSP ); 
	}
    {qtext}*	{
		#ifdef DEBUG
			std::cout << "<QTEXT>" << YYText() << "</QTEXT>";
		#endif // DEBUG		
		return newToken(yylval, YYText(), yy::BisonParser::token::QTEXT  );
	}
}

    /* poczatek cytatu */
\" {
	#ifdef DEBUG
		std::cout << "<quote>\"";
	#endif // DEBUG		
    BEGIN(QUOTE);
    return newToken(yylval, YYText(), yy::BisonParser::token::LDQUOTE );
}

<COMMENT>{
    /* koniec komentarza */
    \)  {
		#ifdef DEBUG
			std::cout << "\"</comment>";
		#endif // DEBUG		
            //quoted-pair
//          if(getLastTokenName()[getLastTokenName().length()] == '\\')
//              return newToken(yylval, YYText(), yy::BisonParser::token::QUOTED_PAIR );
            
		BEGIN(HEADERS);
		return newToken(yylval, YYText(), yy::BisonParser::token::RB );
	}
        
    /* encoded-word */
    "=?"{token}*"?"{token}*"?"({encoded_text}{0,69})"?="    {
		#ifdef DEBUG
			std::cout << "<encoded-word>" << YYText() << "</encoded-word>";
		#endif // DEBUG		
		/* tu wyluskac z niego tokeny i utworzyc liste */
		return newToken(yylval, YYText(), yy::BisonParser::token::ENCODED_WORD );
	}
        
    {ctext}*    {
		#ifdef DEBUG
			std::cout << "<CTEXT>" << YYText() << "</CTEXT>";
		#endif // DEBUG		
	return newToken(yylval, YYText(), yy::BisonParser::token::CTEXT );
	}
}

    /* poczatek komentarza */
\( {
	#ifdef DEBUG
		std::cout << "<comment>\"";
	#endif // DEBUG		
    BEGIN(COMMENT);
    return newToken(yylval, YYText(), yy::BisonParser::token::LB );
}

    /* quoted-pair */
'\\'\" {
	#ifdef DEBUG
		std::cout << "\\\"" << std::endl;
	#endif // DEBUG		
    return newToken(yylval, YYText(), yy::BisonParser::token::QUOTED_PAIR );
}

"\\"{text}  {
	#ifdef DEBUG
		std::cout << "<quoted-pair>" << YYText() << "</quoted-pair>";
	#endif // DEBUG		
    return newToken(yylval, YYText(), yy::BisonParser::token::QUOTED_PAIR );
}   
    
    /* fws */
{WSP}+ {
	#ifdef DEBUG
		std::cout << "<wsp>";
	#endif // DEBUG		
    return newToken(yylval, YYText(), yy::BisonParser::token::WSP );
}

{CRLF}/{WSP} {

    // poprawić - wstawić stan na stos i w wsp powrócić do poprzedniego
	#ifdef DEBUG
		std::cout << "<!!!!! ^^^^^^ crlf>" << std::endl;
	#endif // DEBUG		
    return newToken(yylval, "\r\n", yy::BisonParser::token::CRLF );
}

{CRLF} { 
    //sprawdzenie czy to folded naglowek
//  if( !getFolded() )  BEGIN(HEADERS);

    BEGIN(HEADERS);
	#ifdef DEBUG
		std::cout << "<crlf>" << std::endl;
	#endif // DEBUG		
    return newToken(yylval, "\r\n", yy::BisonParser::token::CRLF ); // EOL
}

.
%%

